package dao;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public abstract class GenericDAO<E> implements InterfaceDAO<E> {
	protected DBConnectionPool dbConnectionPool;
	protected StringBuilder query;
	protected String table;
	protected PreparedStatement statement;
	protected ResultSet rs;
	protected String errorMessage;
	protected boolean errorHappened;

	public GenericDAO() throws ClassNotFoundException, IOException {
		dbConnectionPool = ConnectionPoolManager.getInstance().getConnectionPool();
		errorMessage = "";
		errorHappened = false;
	}

	protected Connection getConnection() {
		return dbConnectionPool.getConnection();
	}

	public void closeConnection(Connection c) {
		closeResultSet();
		closeStatement();
		this.dbConnectionPool.returnConnection(c);
	}

	@Override
	public void insert(E value) {
		try {
			onInsert(value);
		} catch (SQLException ex) {
			onError(ex);
		}
	}

	@Override
	public E select(Integer id) {
		try {
			return onSelect(id);
		} catch (SQLException ex) {
			onError(ex);
			return null;
		}
	}

	@Override
	public List<E> select(E value) {
		try {
			return onSelect(value);
		} catch (SQLException ex) {
			ex.printStackTrace();
			return new ArrayList<E>();
		}
	}

	public String getError() {
		return errorMessage;
	}

	public boolean isErrorHappened() {
		return errorHappened;
	}

	@Override
	public void update(E value) {
		try {
			onUpdate(value);
		} catch (SQLException ex) {
			onError(ex);
		}
	}

	private void onError(SQLException ex) {
		errorHappened = true;
		errorMessage = ex.getMessage();
		ex.printStackTrace();
	}

	protected abstract void onInsert(E value) throws SQLException;

	protected abstract E onSelect(Integer id) throws SQLException;

	protected abstract List<E> onSelect(E value) throws SQLException;

	protected abstract void onUpdate(E value) throws SQLException;

	public abstract int count() throws SQLException;

	protected abstract void prepareUpdateQuery(Connection conn, E value, StringBuilder query)
			throws SQLException;

	/**
	 * Prepares the statement according to the attributes of the document given by
	 * parameter.
	 * @param index, the attributes to build the filters
	 * @throws SQLException
	 */
	protected void prepareUpdateStatement(List<Object> index) throws SQLException {
		for (int i = 0; i < index.size(); i++) {
			if (index.get(i) instanceof Integer)
				statement.setInt(i + 1, (Integer)index.get(i));
			else if (index.get(i) instanceof String)
				statement.setString(i + 1, (String)index.get(i));
			else if (index.get(i) instanceof Float)
				statement.setFloat(i + 1, (Float)index.get(i));
			else if (index.get(i) instanceof Long)
				statement.setLong(i + 1, (Long)index.get(i));
		}
	}

	private void closeResultSet() {
		try {
			if (this.rs != null) {
				this.rs.close();
			}
		} catch (SQLException ex) {
			System.out.println("Error while trying to close the resultset: " + ex.getMessage());
		}
	}

	private void closeStatement() {
		try {
			if (this.statement != null)
				this.statement.close();
		} catch (SQLException ex) {
			System.out.println("Error while trying to close the statement: " + ex.getMessage());
		}
	}
}
